//
//  Map.m
//  TabulaImaginarius2
//
//  Created by TA2 on 4/17/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "Map.h"

@implementation Map

@synthesize name, size, backgroundImage, currentlyMovingTokenLock;

- (id)init {
	currentlyModifyingTiles = [[NSMutableDictionary alloc]  init];
	modifiedTiles = [[NSMutableDictionary alloc]  init];
	tokens = [[NSMutableDictionary alloc]  init];
	currentlyMovingTokenLock = [[NSObject alloc] init];
    loadedBackgroundImage = nil;
    selectedToken = nil;
    objectIdCount = 0;
    tokenIdCount = 0;
	return self;
}

- (void)dealloc {
    [name release];
    name = nil;
    [backgroundImage release];
    backgroundImage = nil;
    [currentlyMovingTokenLock release];
    currentlyMovingTokenLock = nil;
    [currentlyModifyingTiles release];
    currentlyModifyingTiles = nil;
    [modifiedTiles release];
    modifiedTiles = nil;
    [tokens release];
    tokens = nil;
    CGLayerRelease(loadedBackgroundImage);
    loadedBackgroundImage = nil;
    CGLayerRelease(selectedToken);
    selectedToken = nil;
    [super dealloc];
}

- (NSArray *)getAllModifiedTilePoints {
	NSArray *tilePoints;
	@synchronized (modifiedTiles) {
		tilePoints = [NSArray arrayWithArray:[modifiedTiles allKeys]];
	}
	return tilePoints;
}

- (void)modifyTileImage:(UIImage *)image
				atPoint:(CGPoint)point 
			   withPath:(NSString *)path {
	@synchronized (modifiedTiles) {
		Tile *tile = [modifiedTiles objectForKey:[NSValue valueWithCGPoint:point]];
		if (!tile) {
			tile = [[[Tile alloc] init] autorelease];
			tile.point = point;
			[modifiedTiles setObject:tile forKey:[NSValue valueWithCGPoint:point]];
		}
		[tile modifyBufferedImage:image withImagePath:path];
		@synchronized (currentlyModifyingTiles) {
			if (![currentlyModifyingTiles objectForKey:[NSValue valueWithCGPoint:point]]) {
				[currentlyModifyingTiles setObject:tile forKey:[NSValue valueWithCGPoint:point]];
			}
		}
	}
}

- (void)setTileImage:(NSString *)newImageName
			 atPoint:(CGPoint)point {
	@synchronized (modifiedTiles) {
        Tile *tile = [modifiedTiles objectForKey:[NSValue valueWithCGPoint:point]];
        if (newImageName) {
            if (!tile) {
                tile = [[[Tile alloc] init] autorelease];
                tile.point = point;
                [modifiedTiles setObject:tile forKey:[NSValue valueWithCGPoint:point]];
            }
            tile.imageName = newImageName;
            [tile bufferImage];
        } else {
            if (tile) {
                tile.imageName = nil;
                [tile bufferImage];
            }
        }
	}
}

- (NSArray *)getTilePathsAtPoint:(CGPoint)point forPlayer:(Player *)player forLayer:(int)index {
	@synchronized (modifiedTiles) {
		Tile *tile = [modifiedTiles objectForKey:[NSValue valueWithCGPoint:point]];
		if (!tile) {
			tile = [[[Tile alloc] init] autorelease];
			tile.point = point;
			[modifiedTiles setObject:tile forKey:[NSValue valueWithCGPoint:point]];
		}
		NSArray *layers = [tile getLayersForPlayer:player];
		if (layers) {
			return ((DrawingLayer *)[layers objectAtIndex:index]).paths;
		}
	}
	return nil;
}

- (void)addTilePath:(DrawingPath *)path atPoint:(CGPoint)point forPlayer:(Player *)player forLayer:(int)index {
	@synchronized (modifiedTiles) {
		Tile *tile = [modifiedTiles objectForKey:[NSValue valueWithCGPoint:point]];
		if (!tile) {
			tile = [[[Tile alloc] init] autorelease];
			tile.point = point;
			[modifiedTiles setObject:tile forKey:[NSValue valueWithCGPoint:point]];
		}
		[tile addPathToLayer:path forLayer:index forPlayer:player];
	}
	
}

- (void)modifyTilePath:(CGPoint)pathPoint
			   atPoint:(CGPoint)point 
		 withLastPoint:(CGPoint)lastPoint
		  withTileSize:(CGSize)tileSize
		 withLineWidth:(float)lineWidth
			 withColor:(UIColor *)color
			  forLayer:(int)index
         shouldCleanUp:(BOOL)cleanUp {
	[self modifyTilePath:pathPoint atPoint:point withLastPoint:lastPoint withTileSize:tileSize withLineWidth:lineWidth withColor:color withColorTexturePath:nil forLayer:index shouldCleanUp:cleanUp];
}

- (void)modifyTilePath:(CGPoint)pathPoint
			   atPoint:(CGPoint)point 
		 withLastPoint:(CGPoint)lastPoint
		  withTileSize:(CGSize)tileSize
		 withLineWidth:(float)lineWidth
			 withColor:(UIColor *)color
  withColorTexturePath:(NSString *)colorTexturePath
			  forLayer:(int)index
         shouldCleanUp:(BOOL)cleanUp {
	@synchronized (modifiedTiles) {
		Tile *tile = [modifiedTiles objectForKey:[NSValue valueWithCGPoint:point]];
		pathPoint = CGPointMake(pathPoint.x - point.x * tileSize.width, pathPoint.y - point.y * tileSize.height);
		lastPoint = CGPointMake(lastPoint.x - point.x * tileSize.width, lastPoint.y - point.y * tileSize.height);
		if (!tile) {
			tile = [[[Tile alloc] init] autorelease];
			tile.point = point;
			[modifiedTiles setObject:tile forKey:[NSValue valueWithCGPoint:point]];
			[tile addPointToCurrentPath:pathPoint forLayer:index withLineWidth:lineWidth withColor:color withColorTexturePath:colorTexturePath];
		} else {
			DrawingPath *currentPath = [tile getCurrentPath];
			if (currentPath) {
				if (!CGPointEqualToPoint(lastPoint, [[currentPath.points lastObject] CGPointValue])) {
					[self submitChangesToTiles:cleanUp];
					[tile addPointToCurrentPath:lastPoint forLayer:index withLineWidth:lineWidth withColor:color withColorTexturePath:colorTexturePath];
					[tile addPointToCurrentPath:pathPoint forLayer:index withLineWidth:lineWidth withColor:color withColorTexturePath:colorTexturePath];
				} else {
					[tile addPointToCurrentPath:pathPoint forLayer:index withLineWidth:lineWidth withColor:color withColorTexturePath:colorTexturePath];
				}
			} else {
				[tile addPointToCurrentPath:pathPoint forLayer:index withLineWidth:lineWidth withColor:color withColorTexturePath:colorTexturePath];
			}
		}
		@synchronized (currentlyModifyingTiles) {
			if (![currentlyModifyingTiles objectForKey:[NSValue valueWithCGPoint:point]]) {
				[currentlyModifyingTiles setObject:tile forKey:[NSValue valueWithCGPoint:point]];
			}
		}
	}
}

- (void)clearMap {
	@synchronized (modifiedTiles) {
		[modifiedTiles removeAllObjects];
        [tokens removeAllObjects];
        @synchronized (currentlyMovingTokenLock) {
            for (Player *player in [Players sharedAllPlayers]) {
                player.currentlyMovingToken = nil;
            }
        }
        @synchronized (currentlyModifyingTiles) {
            [currentlyModifyingTiles removeAllObjects];
        }
        objectIdCount = 0;
        tokenIdCount = 0;
	}
}

- (void)cancelCurrentPaths {
	@synchronized(currentlyModifyingTiles){
		for (Tile *tile in [currentlyModifyingTiles allValues]){
			[tile deleteCurrentPath];
		}
		[currentlyModifyingTiles removeAllObjects];
	}
}

- (void)scrapCurrentPath:(CGPoint)point {
	@synchronized (currentlyModifyingTiles) {
		Tile *tile = [currentlyModifyingTiles objectForKey:[NSValue valueWithCGPoint:point]];
		if (tile) {
			[tile deleteCurrentPath];
		}
	}
}

- (void)submitChangesToTiles:(BOOL)cleanUp {
	@synchronized (currentlyModifyingTiles) {
		for (NSValue *key in [currentlyModifyingTiles allKeys]) {
			@synchronized (modifiedTiles) {
				Tile *tile = [currentlyModifyingTiles objectForKey:key];
				if (tile) {
					[tile finalizeCurrentPath];
					[tile saveAndReleaseBufferedImage];
                    if (cleanUp) {
                        [self cleanUpDrawingPaths:tile];
                    }
				}
			}
		}
		[currentlyModifyingTiles removeAllObjects];
	}
}

- (int)getNextId:(NSString *)protocolId {
    if ([protocolId isEqualToString:@"OP"]) {
        return ++objectIdCount;
    } else if ([protocolId isEqualToString:@"TP"]) {
        return ++tokenIdCount;
    }
    return -1;
}

- (int)getId:(NSString *)protocolId {
    if ([protocolId isEqualToString:@"OP"]) {
        return objectIdCount;
    } else if ([protocolId isEqualToString:@"TP"]) {
        return tokenIdCount;
    }
}

- (void)setId:(int)idNumber forProtocolId:(NSString *)protocolId {
    if ([protocolId isEqualToString:@"OP"]) {
        objectIdCount = idNumber;
    } else if ([protocolId isEqualToString:@"TP"]) {
        tokenIdCount = idNumber;
    }
}

- (void)addToken:(Token *)token atPoint:(CGPoint)point withTileSize:(CGSize)tileSize {
	@synchronized (modifiedTiles) {
		Tile *tile = [modifiedTiles objectForKey:[NSValue valueWithCGPoint:point]];
		if (!tile) {
			tile = [[[Tile alloc] init] autorelease];
			tile.point = point;
			[modifiedTiles setObject:tile forKey:[NSValue valueWithCGPoint:point]];
		}
		@synchronized (tokens) {
			[tokens setObject:token forKey:token.tokenId];
		}
		[tile.tokenStack addObject:token];
		[token setPoint:(CGPoint){point.x * tileSize.width, point.y * tileSize.height}];
	}
}

- (NSArray *)removeToken:(NSString *)tokenId {
	NSArray *points = nil;
	@synchronized (tokens) {
		Token *token = [tokens objectForKey:tokenId];
		for (Player *player in [Players sharedAllPlayers]) {
			if (player.currentlyMovingToken) {
				player.currentlyMovingToken = nil;
			}
		}
		points = [token getPointsWithOffset:0];
		for (NSValue *pointValue in points) {
			CGPoint point = [pointValue CGPointValue];
			[self removeToken:token fromPoint:point];
		}
		//[tokens setObject:token forKey:token.tokenId];
	}
	return points;
}

- (void)removeToken:(Token *)token fromPoint:(CGPoint)point {
	@synchronized (modifiedTiles) {
		Tile *tile = [modifiedTiles objectForKey:[NSValue valueWithCGPoint:point]];
		if (tile) {
			[tile.tokenStack removeObject:token];
		}
	}
}

- (void)clearLoadedBackgroundImage {
    shouldReloadBackgroundImage = YES;
}

- (void)loadBackgroundImage {
    if (loadedBackgroundImage) {
        CGLayerRelease(loadedBackgroundImage);
        loadedBackgroundImage = nil;
    }
    if (backgroundImage) {
        @synchronized (backgroundImage) {
            [[backgroundImage retain] autorelease];
            UIImage *image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:backgroundImage ofType:@"jpg"]];
            if (!image) {
                image = [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:backgroundImage ofType:@"png"]];
            }
            UIGraphicsBeginImageContext((CGSize){75.0f, 75.0f});
            loadedBackgroundImage = CGLayerCreateWithContext(UIGraphicsGetCurrentContext(), image.size, NULL);
            UIGraphicsEndImageContext();
            UIGraphicsPushContext(CGLayerGetContext(loadedBackgroundImage));
            [image drawInRect:(CGRect){(CGPoint){0, 0}, image.size}];
            UIGraphicsPopContext();
        }
    }
}

- (CGLayerRef)backgroundForPoint:(CGPoint)point {
	CGLayerRef image = nil;
	@synchronized (modifiedTiles) {
		Tile *tile = [modifiedTiles objectForKey:[NSValue valueWithCGPoint:point]];
		if (tile) {
			image = [tile getImage];
			if (!image) {
				if (!loadedBackgroundImage) {
					[self loadBackgroundImage];
					if (loadedBackgroundImage) {
						image = loadedBackgroundImage;
					}
				} else {
                    if (shouldReloadBackgroundImage) {
                        [self loadBackgroundImage];
                        shouldReloadBackgroundImage = NO;
                    }
					image = loadedBackgroundImage;
				}
			}
		} else {
			if (!loadedBackgroundImage) {
				[self loadBackgroundImage];
				if (loadedBackgroundImage) {
					image = loadedBackgroundImage;
				}
			} else {
                if (shouldReloadBackgroundImage) {
                    [self loadBackgroundImage];
                    shouldReloadBackgroundImage = NO;
                }
				image = loadedBackgroundImage;
			}
		}
	}
	return image;
}

- (NSString *)backgroundPathForPoint:(CGPoint)point {
	NSString *backgroundPath = nil;
	@synchronized (modifiedTiles) {
		Tile *tile = [modifiedTiles objectForKey:[NSValue valueWithCGPoint:point]];
		if (tile) {
			backgroundPath = [tile getBackgroundPath];
		}
	}
	return backgroundPath;
}

- (CGLayerRef)drawingTileForPoint:(CGPoint)point withTileSize:(CGSize)tileSize {
	@synchronized (modifiedTiles) {
		Tile *tile = [modifiedTiles objectForKey:[NSValue valueWithCGPoint:point]];
		if (tile) {
			CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
			CGLayerRef drawingBuffer = tile.drawingBuffer;
			if (!drawingBuffer) {
                UIGraphicsBeginImageContext((CGSize){75.0f, 75.0f});
				drawingBuffer = CGLayerCreateWithContext(UIGraphicsGetCurrentContext(), tileSize, NULL);
                UIGraphicsEndImageContext();
				UIGraphicsPushContext(CGLayerGetContext(drawingBuffer));
				for (Player *player in [Players sharedAllPlayers]) {
					for (DrawingLayer *layer in [tile getLayersForPlayer:player]) {
						if (layer.visibleToAllPlayers || [layer.visibleToPlayers containsObject:[Players sharedLocalPlayer]]) {
							for (DrawingPath *path in layer.paths) {
								[self drawPath:path withTileSize:tileSize forPoint:point drawAll:YES];
							}
						}
					}
				}
				tile.drawingBuffer = drawingBuffer;
			} else {
				UIGraphicsPushContext(CGLayerGetContext(drawingBuffer));
				for (Player *player in [Players sharedAllPlayers]) {
					for (DrawingLayer *layer in [tile getLayersForPlayer:player]) {
						if (layer.visibleToAllPlayers || [layer.visibleToPlayers containsObject:[Players sharedLocalPlayer]]) {
							for (DrawingPath *path in layer.newPaths) {
								[self drawPath:path withTileSize:tileSize forPoint:point drawAll:YES];
							}
							[layer.newPaths removeAllObjects];
						}
					}
				}
			}
			DrawingPath *currentTilePath = [tile getCurrentPath];
			if (currentTilePath) {
				[self drawPath:currentTilePath withTileSize:tileSize forPoint:point drawAll:NO];
			}
			UIGraphicsPopContext();
			return drawingBuffer;
		}
	}
	return nil;
}

- (void)drawPath:(DrawingPath *)path withTileSize:(CGSize)tileSize forPoint:(CGPoint)point drawAll:(BOOL)drawAll {
    NSArray *pointList = drawAll ? path.points : path.newPoints;
    if ([pointList count] > 1) {
        if (path.color == [UIColor clearColor]) {
            CGContextSetStrokeColorWithColor(UIGraphicsGetCurrentContext(), [UIColor clearColor].CGColor);
            CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeClear);
        } else {
            CGContextSetStrokeColorWithColor(UIGraphicsGetCurrentContext(), path.color.CGColor);
        }
        CGContextSetLineWidth(UIGraphicsGetCurrentContext(), path.lineWidth);
        for (int i = 0; i < [pointList count] - 1; i++) {
            CGPoint lastPoint = [[pointList objectAtIndex:i] CGPointValue];
            CGPoint currentPoint = [[pointList objectAtIndex:i+1] CGPointValue];
            CGContextBeginPath(UIGraphicsGetCurrentContext());
            CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPoint.x, lastPoint.y);
            CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
            CGContextStrokePath(UIGraphicsGetCurrentContext());
        }
        if (path.color == [UIColor clearColor]) {
            CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeNormal);
        }
    }
}
	

- (void)cleanUpDrawingPaths:(Tile *)tile {
    if (tile) {
        CGLayerRef drawingBuffer = tile.drawingBuffer;
        if (drawingBuffer) {
            UIGraphicsBeginImageContext((CGSize){75.0f, 75.0f});
            UIImage *emptyImage = UIGraphicsGetImageFromCurrentImageContext();
            UIGraphicsEndImageContext();
            
            CGContextRef bitmapContext = CGBitmapContextCreate(NULL, CGImageGetWidth([emptyImage CGImage]), CGImageGetHeight([emptyImage CGImage]), 
                                                               CGImageGetBitsPerComponent([emptyImage CGImage]), CGImageGetBytesPerRow([emptyImage CGImage]),
                                                               CGImageGetColorSpace([emptyImage CGImage]), CGImageGetBitmapInfo([emptyImage CGImage]));
            if (bitmapContext) {
                CGContextDrawLayerAtPoint(bitmapContext, CGPointZero, drawingBuffer);
                typedef struct
                {
                    uint8_t red;
                    uint8_t green;
                    uint8_t blue;
                    uint8_t alpha;
                } PixelData;
                PixelData *pixels = CGBitmapContextGetData(bitmapContext);
                size_t pixelCount = CGImageGetWidth([emptyImage CGImage]) * CGImageGetHeight([emptyImage CGImage]);
                int nonEmptyPixels = 0;
                for(size_t i = 0; i < pixelCount; i++)
                {
                    PixelData p = pixels[i];
                    if (p.alpha > 0) {
                        nonEmptyPixels++;
                    }
                }
                if (nonEmptyPixels < 5) {
                    [tile clearVisiblePaths];
                }                 
                CGContextRelease(bitmapContext);
            }
        }
    }
}

- (CGLayerRef)graphicsLayerForMovingTokenAtPoint:(CGPoint)point withViewId:(NSString *)viewId {
	CGLayerRef image = nil;
	@synchronized (currentlyMovingTokenLock) {
		for (Player *player in [Players sharedAllPlayers]) {
			if (player.currentlyMovingToken) {
                if ([player.currentlyMovingToken.protocolId isEqualToString:viewId]) {
                    image = [player.currentlyMovingToken getImageAtPoint:point withOffset:0];
                    if (image) {
                        break;
                    }	
                }
			}
		}
		return image;
	}
}

- (NSMutableArray *)graphicsLayersForTokenAtPoint:(CGPoint)point withViewId:(NSString *)viewId {
	NSMutableArray *images = [[[NSMutableArray alloc] init] autorelease];
	CGLayerRef image;
	@synchronized (modifiedTiles) {
		Tile *tile = [modifiedTiles objectForKey:[NSValue valueWithCGPoint:point]];
		if (tile) {
			for (Token *token in tile.tokenStack) {
                if ([token.protocolId isEqualToString:viewId]) {
                    image = [token getImageAtPoint:point withOffset:0];
                    if (image) {
                        [images addObject:(NSObject *)image];
                        if ([token getIdLayerAtPoint:point]) {
                            [images addObject:(NSObject *)([token getIdLayerAtPoint:point])];
                        }
                    }
                }
			}
		}
	}
	return images;
}

- (Token *)tokenForPoint:(CGPoint)point withViewId:(NSString *)viewId {
	@synchronized (modifiedTiles) {
		Tile *tile = [modifiedTiles objectForKey:[NSValue valueWithCGPoint:point]];
		if (tile) {
            for (int index = [tile.tokenStack count] - 1; index > -1; index--) {
                if ([((Token *)[tile.tokenStack objectAtIndex:index]).protocolId isEqualToString:viewId]) {
                    return [tile.tokenStack objectAtIndex:index];
                }
            }
		}
	}
	return nil;	
}

- (NSArray *)tokenStackForPoint:(CGPoint)point {
	@synchronized (modifiedTiles) {
		Tile *tile = [modifiedTiles objectForKey:[NSValue valueWithCGPoint:point]];
		if (tile) {
			return [NSArray arrayWithArray:tile.tokenStack];
		}
	}
	return nil;	
}

- (BOOL)saveMap {
	BOOL returnValue;
	NSString *archivePath;
	@synchronized (modifiedTiles) {
		@synchronized (currentlyModifyingTiles) {
			@synchronized (currentlyMovingTokenLock) {
                @synchronized (tokens) {
                    if(TARGET_IPHONE_SIMULATOR) {
                        archivePath = [[[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:name] stringByAppendingString:@".save"];
                    } else {
                        NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
                        archivePath = [[documentsDirectory stringByAppendingPathComponent:name] stringByAppendingString:@".save"];
                    }
                    returnValue = [NSKeyedArchiver archiveRootObject:self toFile:archivePath];
                }
			}
		}
	}
	return returnValue;
}

+ (Map *)loadMap:(NSString *)mapName {
    if (mapName) {
        NSString *archivePath;
        if(TARGET_IPHONE_SIMULATOR) {
            archivePath = [[[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:mapName] stringByAppendingString:@".save"];
        } else {
            NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
            archivePath = [[documentsDirectory stringByAppendingPathComponent:mapName] stringByAppendingString:@".save"];
        }
        if ([[NSFileManager defaultManager] fileExistsAtPath:archivePath]) {
            Map *map = [NSKeyedUnarchiver unarchiveObjectWithFile:archivePath];
            if (map) {
                return map;
            }
        }
    }
	return nil;
}

- (void)encodeWithCoder:(NSCoder *)coder {
    [coder encodeObject:name forKey:@"mapName"];
    [coder encodeCGSize:size forKey:@"mapSize"];
    [coder encodeObject:backgroundImage forKey:@"mapBackgroundImage"];
    [coder encodeInt:[[modifiedTiles allKeys] count] forKey:@"mapModifiedCount"];
    int index = 0;
    for (NSValue *pointValue in [modifiedTiles allKeys]) {
        CGPoint point = [pointValue CGPointValue];
        [coder encodeObject:[modifiedTiles objectForKey:pointValue] forKey:[@"mapModifiedTile" stringByAppendingString:[NSString stringWithFormat:@"%d", index]]];
        [coder encodeCGPoint:point forKey:[@"mapModifiedPoint" stringByAppendingString:[NSString stringWithFormat:@"%d", index]]];
        index++;
    }
    [coder encodeObject:modifiedTiles forKey:@"mapModifiedTiles"];
    [coder encodeInt:objectIdCount forKey:@"mapObjectIdCount"];
    [coder encodeInt:tokenIdCount forKey:@"mapTokenIdCount"];
}

- (id)initWithCoder:(NSCoder *)coder {
    self = [super init];
    self.name = [coder decodeObjectForKey:@"mapName"];
    self.size = [coder decodeCGSizeForKey:@"mapSize"];
    self.backgroundImage = [coder decodeObjectForKey:@"mapBackgroundImage"];
    currentlyModifyingTiles = [[NSMutableDictionary alloc] init];
    currentlyMovingTokenLock = [[NSObject alloc] init];
    modifiedTiles = [[NSMutableDictionary alloc] init];
    int modifiedCount = [coder decodeIntForKey:@"mapModifiedCount"];
    for (int index = 0; index < modifiedCount; index++) {
        Tile *tile = [coder decodeObjectForKey:[@"mapModifiedTile" stringByAppendingString:[NSString stringWithFormat:@"%d", index]]];
        NSValue *pointValue = [NSValue valueWithCGPoint:[coder decodeCGPointForKey:[@"mapModifiedPoint" stringByAppendingString:[NSString stringWithFormat:@"%d", index]]]];
        if (tile && pointValue) {
            [modifiedTiles setObject:tile forKey:pointValue];
        }
    }
    objectIdCount = [coder decodeIntForKey:@"mapObjectIdCount"];
    tokenIdCount = [coder decodeIntForKey:@"mapTokenIdCount"];
    tokens = [[NSMutableDictionary alloc] init];
    for (Tile *tile in [modifiedTiles allValues]) {
        for (Token *token in tile.tokenStack) {
            [tokens setObject:token forKey:token.tokenId];
        }
    }
    loadedBackgroundImage = nil;
    selectedToken = nil;
    return self;
}

- (CGPoint)convertCoordsToGridCoords:(CGPoint)coords withTileSize:(CGSize)tileSize {
	uint x;
	uint y;
	x = (uint)coords.x / (uint)tileSize.width;
	y = (uint)coords.y / (uint)tileSize.height;
	return CGPointMake(x, y);
}

@end
